The Role of Idempotency in API Design
Let's learn about idempotency and its need in API design.
Introduction#
Idempotence is a concept where clients get the same response against multiple identical requests. Using idempotence, calling endpoints any number of times results in the call occurring only once. This is an important concept because it ensures that if a client repeatedly sends the same request, then the server state remains the same after the first call.
Before we discuss a practical example of idempotence, let's understand idempotence with the help of the following mathematical function:
For the equation above, if x = 4, then f(x) is equal to 70. The f(x) value will not change as long as x remains 4. This means that the equation above is idempotent for x = 4. In the same way, an API is idempotent if the response remains the same for all identical incoming requests.
Let’s consider the Twitter API that deletes a Tweet. Upon receiving the client's request for the first time, the server should delete the Tweet. For similar subsequent requests, the server's state should not change, and the response for the client should remain the same. Take a look at the illustration below for an illustration of the described behavior.
In the illustration above, the server performed the intended action only once. But on the second request, the server sent the same response from the cache. Therefore, it doesn’t matter how many requests a client initiates to delete a particular Tweet, it gets the same response from the server. The table below highlights some advantages and disadvantages of APIs that are idempotent.
Non-idempotent API | Idempotent API's advantages | Idempotent API's disadvantages |
Overloads the server by repeatedly requesting for same resources (multiple requests) | Reduces load on the server | Extra storage is required to cache the responses and keys of the responses |
Causes data redundancy by accepting all the requests and sending the same data repeatedly | Eliminates data redundancy | Custom scheduling job (custom code) is required to define TTL and then clear the data from storage |
Due to the repetition of the same requests, networks can get busy, and congested | Avoids network congestion | In case of using third-party scheduling jobs (TTL and clearing storage jobs), it adds the overhead of integrating them |
These are resource wastages because they’re occupied in responding to the same requests again and again. | Efficient resource utilization | - |
Now, we’ll go into the details of some examples of the cases specified above.
Why does idempotence matter in API design?#
One attribute of a good API is that it functions according to the client's expectations and keeps a consistent state. This attribute can be achieved by idempotence. Let's highlight the primary reasons for creating an idempotent API:
Reliability: Provides reliability when identical requests are forwarded to the servers by a client.
Consistency: Ensures consistency by avoiding duplicate operations (when not required) performed due to identical requests. For example, duplicate counts of likes on some news items might artificially prop it up, or duplicate charges on a credit card will have inconsistencies between a client's record and the credit card record.
Clients often request the same thing multiple times because they perceive their initial requests as unsuccessful, typically due to a lack of response. An unsuccessful attempt means the request reaches the server, and the server performs the intended operation. However, the client doesn’t receive a response or confirmation due to network or server errors or a timeout. So, in this case, the client forwards the identical request again, and the server will repeat that operation, which may cause data duplication (for example, when clients repeatedly request to add data to the database). This happens when unique key constraints are not implemented in the system. We can add unique key constraints to avoid duplicate data insertion in the database, which is the simplest approach to implementing idempotence.
Note: Initiating the same request again is called a retry. On numerous occasions, retries can be automatic. This course has a detailed lesson on retries.
Example#
We saw issues that might arise due to duplicated delete requests for a Tweet. But even if our API was not idempotent, and Tweet IDs are never repeated, a repeated delete request can’t be too detrimental aside from just wasting resources on the server-side. But now we’ll see another example where we must deal with duplicates.
Let's understand the data redundancy problem discussed above with the help of a real example where the client has to transfer money to a particular bank account using a mobile application:
First, the client forwards the request (which uses the POST method) using the bank API to the server.
The bank's server receives the particular request and performs the intended action. After successfully operating, the server sends a response to the specified client.
Due to any network failure or timeout, the client doesn’t get a response from the bank's server and sends the same request again.
The server receives the same request, performs the intended operation again, and responds to the client.
The client again does not receive the feedback and repeats the same request for the third time.
After the last request (step 5), assume the client finally receives the feedback from the server. But the server has transferred the specified amount three times to a particular account. As a result, the system becomes unreliable because the data changes for each identical request without the client's intention. In the scenario above, the network issue caused an error in sending responses to the client and made the system unreliable. In cases like this, duplicate operations can be avoided using idempotence. Similarly, data duplication can happen while inserting data or records into the system. We can see how idempotence is important here to avoid data and operation duplication on the server.
Note: Making HTTP methods idempotent enables the idempotency attribute of the API, which eventually helps to achieve reliability. When we say API is reliable, it should have consistency, availability, and low latency attributes. Idempotence helps to achieve these attributes.
What are idempotent HTTP methods?#
This section highlights HTTP methods that inherently have idempotent properties, such as DELETE and PUT. When clients forward identical requests one or many times using any of these methods, the result does not change from the request that was given the first time. Similarly, some HTTP methods such as GET, TRACE, and HEAD are idempotent, which means clients can make multiple requests freely, so long as no changes happen to the data. They’re also called safe methods because they don’t alter the data stored in the server's database. In simple words, a method is safe if it performs read-only operations. The methods that are idempotent and unsafe can alter the server's state, so a developer should carefully handle the use of these methods. Authentication and authorization are applied to check whether the intended operation to change the server's state is authorized for a specific client. Let's take a look at the table below to recognize the idempotent, non-idempotent, safe, and unsafe methods:
HTTP methods | Idempotent | Safe |
| Yes | Yes |
| Yes | Yes |
| Yes | Yes |
| Yes | No |
| Yes | No |
| No | No |
| No | No |
In the next section, we’ll also discuss methods that are not inherently idempotent and safe.
How to make methods idempotent?#
Some HTTP methods, such as POST and PATCH, are not inherently idempotent and safe. Let's see the illustration below to understand the side effects of identical requests using the POST method.
The illustration above shows that the client sends the POST request to the server, and the server stores the content in its database. When the client does not get a confirmation response from the server, it sends the same request to the server again. If the server has no strategy to deal with this, it executes the same request again and stores the same data in the database. Therefore, we can see that using POST to add data can lead to redundant data in the storage.
To handle this issue, we’ll have to make all methods idempotent, which eventually helps the server to identify repeated identical requests. To address this issue, the client can add a unique request identifier key as part of the HTTP header. The client generates this unique random 128 bit number, called a Version 4 UUID (Universal Unique Identifier). When the server receives client requests, it has to store unique idempotency keys loaded with the requests in its database. So, when a client makes the same request again, the server can check its database. If the request has already been addressed, the server ignores it and responds with the previously cached results. Otherwise, it stores the idempotency key in the database and commits the request. Let's take a look at the example below to understand better:
1 of 8
2 of 8
3 of 8
4 of 8
5 of 8
6 of 8
7 of 8
8 of 8
In the slides given above, we assumed that the client didn’t get a response on the first request from the server due to any network error, but it got a response on the second request. Let's see how the idempotency key is packed with the request in the following curl command.
In the curl command above, the client is depositing money in the particular account using the idempotency key generated by the Version 4 UUID.
Quiz
Question
Is there a need to explicitly make each HTTP method idempotent?
Most of the methods (GET, TRACE, HEAD, DELETE, and PUT ) are already idempotent or safe. So, we do not need to explicitly make them idempotent. On the other hand, some methods are kept non-idempotent by nature. For example, the POST and PATCH methods are non-idempotent, but they can be made idempotent to avoid redundancy and other side effects.
Making every method idempotent by default may not always be the desired behavior. Methods like POST are kept non-idempotent by default because they should result in a server state change. They can be made idempotent explicitly where the idempotent behavior is required by the system.
Although Version 4 UUID provides a unique long random number, the chances of the same Version 4 UUID is one in a billion. The problem occurs when the server has to index millions of long numbers in the database. Therefore, scalability costs a lot in this case because it needs a lot of storage to save as much data (even if they are duplicate records). Let's discuss how we lower this cost by reducing the storage time of these unique IDs on the server. We know that there are a few HTTP methods (POST and PATCH) for which we need idempotency keys. Consider a commercial bank server receiving 10,000 unique transactions (adding or deleting a specified amount) in a single second. The server will need to store 10,000 unique IDs per second for all transaction requests. We’ll have to find how long a server has to store the unique IDs (idempotency keys) in the database.
We can store the idempotency keys in the server's database until the session ends. Although the session time is usually from 1 minute to an hour, it can be kept live for a maximum of 24 hours. Let's assume the bank application user requests the same transaction again after not receiving a response due to a network error. The server will receive these identical requests and give cached responses after matching the idempotency keys. When sessions end, the server deletes all the keys related to the particular sessions from the databases. On the other hand, the keys will not be used anymore by the user and will be removed. The users will generate the new keys for the new sessions.
Making POST and PATCH operations idempotent is needed when security is a concern, because they can alter the server's state. Additionally, other applications (Twitter, Instagram, and so on) make these methods idempotent for a better user experience by avoiding data redundancy and lowering the load on resources and the need for extra memory for databases.
(Select all that apply.) Determine which HTTP method(s) is/are made explicitly idempotent by using idempotency keys generated by the Version 4 UUID.
GET
We do not need to use an idempotency key for GET because it is already a safe and idempotent method.
POST
Yes, the POST method must be made idempotent because it causes data duplication.
DELETE
The DELETE method is already inherently idempotent, so we do not need to make it explicitly idempotent.
PUT
Calling the PUT method one or many times continuously has the same effect. So, we do not need the idempotency key for this explicitly.
Summary#
This lesson introduced idempotence to avoid duplicate operations for efficient resource utilization, make the API reliable, and to avoid network congestion. We also learned how to make a method idempotent if it is not idempotent by default, and how servers work if idempotence is implemented. Idempotence enables a retry mechanism and avoids performing duplicate operations, resulting in less load on the server and a less congested network. We have a detailed lesson that discusses how to manage retries and prevent congestion in the network.
Cookies and Sessions
Server-Side Rendering vs. Client-Side Rendering